import { MarkConfig, NodeConfig } from "@valuation/core";
import {
  AnyConfig,
  Attribute,
  Attributes,
  ExtensionAttribute,
  Extensions,
  GlobalAttributes,
} from "../types.ts";
import { getExtensionField } from "./getExtensionField.ts";
import { splitExtensions } from "./splitExtensions.ts";

/**
 * 获取‘ addAttribute ’和‘ addGlobalAttribute ’中定义的所有扩展属性的列表
 * @param extensions List of extensions
 */
export function getAttributesFromExtensions(
  extensions: Extensions,
): ExtensionAttribute[] {
  const extensionAttributes: ExtensionAttribute[] = [];
  const { nodeExtensions, markExtensions } = splitExtensions(extensions);
  const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
  const defaultAttribute: Required<Attribute> = {
    default: null,
    isRequired: false,
  };

  extensions.forEach((extension) => {
    const context = {
      name: extension.name,
      options: extension.options,
      storage: extension.storage,
    };

    const addGlobalAttributes = getExtensionField<
      AnyConfig["addGlobalAttributes"]
    >(
      extension,
      "addGlobalAttributes",
      context,
    );

    if (!addGlobalAttributes) {
      return;
    }

    // TODO: remove `as GlobalAttributes`
    const globalAttributes = addGlobalAttributes() as GlobalAttributes;

    globalAttributes.forEach((globalAttribute) => {
      globalAttribute.types.forEach((type) => {
        Object
          .entries(globalAttribute.attributes)
          .forEach(([name, attribute]) => {
            extensionAttributes.push({
              type,
              name,
              attribute: {
                ...defaultAttribute,
                ...attribute,
              },
            });
          });
      });
    });
  });

  nodeAndMarkExtensions.forEach((extension) => {
    const context = {
      name: extension.name,
      options: extension.options,
      storage: extension.storage,
    };

    const addAttributes = getExtensionField<
      NodeConfig["addAttributes"] | MarkConfig["addAttributes"]
    >(
      extension,
      "addAttributes",
      context,
    );

    if (!addAttributes) {
      return;
    }

    // TODO: remove `as Attributes`
    const attributes = addAttributes() as Attributes;

    Object
      .entries(attributes)
      .forEach(([name, attribute]) => {
        const mergedAttr = {
          ...defaultAttribute,
          ...attribute,
        };

        if (attribute?.isRequired && attribute?.default === undefined) {
          delete mergedAttr.default;
        }

        extensionAttributes.push({
          type: extension.name,
          name,
          attribute: mergedAttr,
        });
      });
  });

  return extensionAttributes;
}
